﻿#include "CameraDushen.h"
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
#include <QTimer>
#include <QMutexLocker>
#include <QDebug>
#pragma execution_character_set("utf-8")

/* 图像回调函数 */
dvpInt32 ImageCallBackEx(dvpHandle handle, dvpStreamEvent event, void* pContext, dvpFrame* pFrameInfo, void* pData){
    if (pFrameInfo == NULL){
        printf("pFrame is NULL\n");
        return -1;
    }
    auto pCamera = static_cast<CameraBase*>(pContext);
    if(pCamera==nullptr)
        return -1;
    ImageInfo imgInfo={0};
    if(pFrameInfo->bRotateState){
        imgInfo.nWidth = pFrameInfo->iHeight;
        imgInfo.nHeight = pFrameInfo->iWidth;
    }else {
        imgInfo.nWidth = pFrameInfo->iWidth;
        imgInfo.nHeight = pFrameInfo->iHeight;
    }
    imgInfo.pData = (unsigned char *)pData;
    imgInfo.nDataLen = pFrameInfo->uBytes;
    imgInfo.enPixelType = pFrameInfo->format;
    pCamera->preProcessImage(imgInfo);
    return 0;
}
/*掉线事件回调函数*/
dvpInt32 DisconnectedEventCallBack(dvpHandle handle, dvpEvent event, void *pContext, dvpInt32 param, struct dvpVariant *pVariant)
{
    auto pCamera = static_cast<CameraBase*>(pContext);
    if(pCamera == nullptr)
        return 0;
    if(event == EVENT_DISCONNECTED){
        printf("EVENT_DISCONNECTED");
        pCamera->setStatus(CS_Unavailable);
        emit pCamera->sigErrorOccured(pCamera->getName(), CEC_Remove);
    }
    
    return 0;
}


void CameraDushen::saveParamToUserSet()
{
    //save params to user set
    dvpSaveUserSet(handle, USER_SET_1);
}

bool CameraDushen::isOnline()
{
    bool isol;
    dvpStatus nRet = dvpIsOnline(handle, &isol);
    if (DVP_STATUS_OK != nRet){
        printf("dvpOpen failed, nRet [0x%x]\n", nRet);
        return false;
    }
    return isol;
}

CameraDushen::CameraDushen():CameraBase()
{
    connect(this,&CameraBase::sigErrorOccured,this,[&](const QString &name, CameraErrorCode errCode){
        if(errCode == CEC_Remove){
            reStart();
        }
    });
}

CameraDushen::~CameraDushen()
{

}

void CameraDushen::initialize()
{
    QMutexLocker mutexLocker(&mLock);
    setStatus(CS_Unavailable);
    // Enum device
    QStringList camList = enumCameraList();
    mIndex = camList.indexOf(mCamConfig.name);
    if (mIndex == -1){
        printf("%s unavailable:\n", mCamConfig.name.toStdString().c_str());
        return ;
    }
    setStatus(CS_Closed);
}

void CameraDushen::openCamera()
{
    if(mStatus != CS_Closed)
        return;
    // Open device
    /* 打开设备 */
    dvpStatus nRet = dvpOpen(mIndex, OPEN_NORMAL, &handle);
    if (DVP_STATUS_OK != nRet){
        printf("dvpOpen failed, nRet [0x%x]\n", nRet);
        return;
    }

    /* 在打开相机之后，开启视频流之前注册事件回调函数，启动一个专门的线程以dvpGetFrame（同步采集）的方式获取图像 */
    nRet = dvpRegisterStreamCallback(handle, ImageCallBackEx, STREAM_EVENT_FRAME_THREAD, this);
    if (nRet != DVP_STATUS_OK){
        printf("dvpRegisterStreamCallback failed with err:%d\r\n", nRet);
        return;
    }

    // Register disconnected exception callBack
    nRet = dvpRegisterEventCallback(handle, DisconnectedEventCallBack,EVENT_DISCONNECTED, this);
    if (DVP_STATUS_OK != nRet){
        printf("RegisterExceptionCallBack fail! nRet [0x%x]\n", nRet);
        return;
    }
    mCamConfig.isColor = isColor(getNodeEnum("PixelFormat"));
    mCamConfig.isLine = getNodeEnum("DeviceScanType") == 1;
    setStatus(CS_Opened);
}

void CameraDushen::startGrabbing()
{
    if(mStatus == CS_Opened){
        auto nRet = dvpStart(handle);
        if (DVP_STATUS_OK != nRet){
            printf("Start Grabbing fail! nRet [0x%x]\n", nRet);
            return;
        }
        setStatus(CS_Grabbing);
    }
}

void CameraDushen::stopGrabbing()
{
    // Stop grab image
    if(mStatus == CS_Grabbing){
        auto nRet = dvpStop(handle);
        if (DVP_STATUS_OK != nRet){
            printf("Stop Grabbing fail! nRet [0x%x]\n", nRet);
        }
        setStatus(CS_Opened);
    }
}

void CameraDushen::closeCamera()
{
    if(mStatus == CS_Opened){
        setStatus(CS_Closing);
        // Close device
        auto nRet = dvpClose(handle);
        if (DVP_STATUS_OK != nRet){
            printf("Close Device fail! nRet [0x%x]\n", nRet);
        }
        setStatus(CS_Closed);
    }
}

void CameraDushen::releaseCamera()
{
    // Destroy handle
    setStatus(CS_Unavailable);
}



void CameraDushen::preProcessImage(const ImageInfo &imgInfo)
{
    if(imgInfo.pData == nullptr || mStatus != CS_Grabbing){
        return;
    }
    if (isColor((dvpImageFormat)imgInfo.enPixelType)){
        bool isNeedConvert = (dvpImageFormat)imgInfo.enPixelType == FORMAT_RGB24 ? false : true;
        unsigned char *pConvertData = NULL;
        unsigned int nConvertDataSize = 0;
        nConvertDataSize = imgInfo.nWidth * imgInfo.nHeight * 3;
        pConvertData = (unsigned char*)malloc(nConvertDataSize);
        if (NULL == pConvertData){
            printf("malloc pConvertData fail!\n");
            return;
        }
        // auto nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
        // if (DVP_STATUS_OK != nRet){
        //     printf("Convert Pixel Type fail! nRet [0x%x]\n", nRet);
        //     emit sigErrorOccured(mCamConfig.name, CEC_ImgError);
        // } else{
        //     QImage image(pConvertData,imgInfo.nWidth,imgInfo.nHeight, QImage::Format_RGB888);
        //     emit sigImage(mCamConfig.name, image.copy());
        // }
        // if(pConvertData){
        //     free(pConvertData);
        //     pConvertData = NULL;
        // }
    }else{
        QImage image(imgInfo.pData, imgInfo.nWidth, imgInfo.nHeight, imgInfo.nWidth * 1, QImage::Format_Indexed8);
        emit sigImage(mCamConfig.name, image.copy());
    }
}

QStringList CameraDushen::enumCameraList()
{
    QStringList camNameList;
    dvpUint32 count = 0;
    dvpRefresh(&count);
    for (dvpUint32 i = 0; i < count; i++){
        dvpCameraInfo info;
        if(dvpEnum(i, &info) == DVP_STATUS_OK){
            QString cameraName = QString(reinterpret_cast<char*>(info.UserID));
            camNameList << cameraName;
        }
    }
    return camNameList;
}
int CameraDushen::getPixelFormat()
{
    return getNodeEnum("PixelFormat");
}
bool CameraDushen::isColor(int pixelFormat)
{
    switch((dvpImageFormat)pixelFormat){
    case FORMAT_BAYER_BG:
    case FORMAT_BAYER_GB:
    case FORMAT_BAYER_GR:
    case FORMAT_BAYER_RG:
    case FORMAT_BGR24:
    case FORMAT_BGR32:
    case FORMAT_BGR48:
    case FORMAT_BGR64:
    case FORMAT_RGB24:
    case FORMAT_RGB32:
    case FORMAT_RGB48:
    case FORMAT_YUV411:
    case FORMAT_YUV422:
    case FORMAT_YUV444:
    case FORMAT_YUV420:
    case FORMAT_B8_G8_R8:
    case FORMAT_B16_G16_R16:
        return true;
    case FORMAT_MONO:
    case FORMAT_MONO1:
        return false;
    default:
        return false;
    }
}

int CameraDushen::getScanType()
{
    return getNodeEnum("DeviceScanType");
}

bool CameraDushen::isLineScan(int scanType)
{
    return scanType == 1;
}

void CameraDushen::setFrameRate(const QString &camName, const float &rate)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeFloat("AcquisitionFrameRate", rate);
}

void CameraDushen::setFrameRateControl(const QString &camName, const bool &control)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeBool("AcquisitionFrameRateEnable", control);
}

void CameraDushen::setPreDiv(const QString &camName, const int &preDiv)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeInt("PreDivider", preDiv);
}

void CameraDushen::setMul(const QString &camName, const int &mul)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeInt("Multiplier", mul);

}

void CameraDushen::setPostDiv(const QString &camName, const int &postDiv)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeInt("PostDivider", postDiv);
}

void CameraDushen::triggerSoftware()
{
    if(getNodeEnum("TriggerMode") != TRIGGER_IN_OFF
        && getNodeEnum("TriggerSource") == TRIGGER_SOURCE_SOFTWARE){
        execCommand("TriggerSoftware");
    }
}

void CameraDushen::setFrameTriggerMode(const QString &camName, const CameraTriggerType &type)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeEnum("TriggerSelector", 6);
    setTriggerMode(camName, type);
}

void CameraDushen::setFrameTriggerDelay(const QString &camName, const float &delay)
{
    if(camName != mCamConfig.name)
        return;
    if(!(handle))
        return;
    setNodeEnum("TriggerSelector", 6);
    setNodeFloat("TriggerDelay", delay);
}

void CameraDushen::setLineTriggerMode(const QString &camName, const CameraTriggerType &type)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeEnum("TriggerSelector", 9);
    setTriggerMode(camName, type);
}


void CameraDushen::setTriggerMode(const QString &camName, const CameraTriggerType &type)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    if(type == CTT_Off){
        setNodeEnum("TriggerMode", TRIGGER_IN_OFF);
    } else {
        setNodeEnum("TriggerMode", TRIGGER_POS_EDGE);
        switch (type) {
        case CTT_Software:
            setNodeEnum("TriggerSource",TRIGGER_SOURCE_SOFTWARE);
            break;
        // case CTT_FrequencyConverter:
        //     setNodeEnum("TriggerSource",MV_TRIGGER_SOURCE_FrequencyConverter);
        //     break;
        case CTT_Line0:
        case CTT_Line1:
        case CTT_Line2:
        case CTT_Line3:
            setNodeEnum("TriggerSource",type - CTT_Line0 + 1);
            break;
        default:
            break;
        }
    }
}

void CameraDushen::setExposureTime(const QString& camName, const float &value)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeFloat("ExposureTime", value);
    saveParamToUserSet();
}

void CameraDushen::setGainMode(const QString &camName, const int &gainMode)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeEnum("GainAuto", gainMode);
}

void CameraDushen::setExposureAutoMode(const QString &camName, const int &expMode)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeEnum("ExposureAuto", expMode);
}
void CameraDushen::setGain(const QString& camName, const float &gain)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeFloat("Gain", gain);
}

void CameraDushen::setWhiteBalanceMode(const QString &camName, const int &mode)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeEnum("BalanceWhiteAuto", mode);
}

void CameraDushen::setWhiteBalanceR(const QString& camName,  const int &value)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeEnum("BalanceRatioSelector", 0);
    setNodeInt("BalanceRatio", value);
    saveParamToUserSet();
}

void CameraDushen::setWhiteBalanceG(const QString& camName,  const int &value)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeEnum("BalanceRatioSelector", 1);
    setNodeInt("BalanceRatio", value);
    saveParamToUserSet();
}

void CameraDushen::setWhiteBalanceB(const QString& camName,  const int &value)
{
    if(camName != mCamConfig.name)
        return;
    if(!isOnline())
        return;
    setNodeEnum("BalanceRatioSelector", 2);
    setNodeInt("BalanceRatio", value);
    saveParamToUserSet();
}
void CameraDushen::setWidth(const QString& camName, const int& width)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeInt("Width", width);
}
void CameraDushen::setHeight(const QString& camName, const int& height)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeInt("Height", height);
}
void CameraDushen::setOffsetX(const QString& camName, const int& off_x)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeInt("OffsetX", off_x);
    saveParamToUserSet();
}
void CameraDushen::setOffsetY(const QString& camName, const int& off_y)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus == CS_Unavailable)
        return;
    setNodeInt("OffsetY", off_y);
    saveParamToUserSet();
}

void CameraDushen::setReverseX(const QString &camName, const bool &reverse_x)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeBool("ReverseX", reverse_x);
    saveParamToUserSet();
}

void CameraDushen::setReverseY(const QString &camName, const bool &reverse_y)
{
    if(camName != mCamConfig.name)
        return;
    if(mStatus != CS_Opened)
        return;
    setNodeBool("ReverseY", reverse_y);
    saveParamToUserSet();
}

void CameraDushen::setNodeBool(const char* nodeName, bool bValue)
{
    int nRet = dvpSetBoolValue(handle, nodeName, bValue);
    if(DVP_STATUS_OK != nRet){
        printf("%s set %s fail! ErrorCode:[0x%x] value:%d\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet, bValue);
    }
}

void CameraDushen::setNodeEnum(const char* nodeName, const int& nValue)
{
    int nRet = dvpSetEnumValue(handle,nodeName,nValue);
    if(DVP_STATUS_OK != nRet){
        printf("%s set %s fail! ErrorCode:[0x%x] value:%d\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet, nValue);
    }
}

void CameraDushen::setNodeInt(const char* nodeName, const int& iValue)
{
    int nRet = dvpSetIntValue(handle, nodeName, iValue);
    if(DVP_STATUS_OK != nRet){
        printf("%s set %s fail! ErrorCode:[0x%x] value:%d\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet, iValue);
    }
}

void CameraDushen::setNodeFloat(const char *nodeName, const float &fValue)
{
    int nRet = dvpSetFloatValue(handle, nodeName, fValue);
    if(DVP_STATUS_OK != nRet){
        printf("%s set %s fail! ErrorCode:[0x%x] value:%d\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet, fValue);
    }
}

bool CameraDushen::getNodeBool(const char *nodeName)
{
    bool val = false;
    int nRet = dvpGetBoolValue(handle, nodeName, &val);
    if(DVP_STATUS_OK != nRet){
        printf("%s get %s fail! ErrorCode:[0x%x]\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet);
    }
    return val;
}

int CameraDushen::getNodeEnum(const char *nodeName)
{
    unsigned int pCurEnumValue;
    unsigned int nszSupportValue[64];
    unsigned int pnSupportNum;
    int nRet = dvpGetEnumValue(handle, nodeName, &pCurEnumValue, nszSupportValue, &pnSupportNum);
    if(DVP_STATUS_OK != nRet){
        printf("%s get %s fail! ErrorCode:[0x%x]\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet);
        pCurEnumValue = 0;
    }
    return pCurEnumValue;
}

IntValue CameraDushen::getNodeInt(const char *nodeName)
{
    IntValue valRe;

    dvpIntDescr val;
    int64_t curVal;
    int nRet = dvpGetIntValue(handle, nodeName, &curVal, &val);
    if(DVP_STATUS_OK != nRet){
        printf("%s get %s fail! ErrorCode:[0x%x]\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet);
        curVal = 0;
    }
    valRe.val = curVal;
    valRe.min = val.iMin;
    valRe.max = val.iMax;
    valRe.inc = val.iStep;
    return valRe;
}

FloatValue CameraDushen::getNodeFloat(const char *nodeName)
{
    FloatValue valRe;
    float valNow;
    dvpFloatDescr val;
    int nRet = dvpGetFloatValue(handle, nodeName, &valNow, &val);
    if(DVP_STATUS_OK != nRet){
        printf("%s get %s fail! ErrorCode:[0x%x]\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet);
    }
    valRe.val = valNow;
    valRe.min = val.fMin;
    valRe.max = val.fMax;
    return valRe;
}

void CameraDushen::execCommand(const char *nodeName)
{
    int nRet = dvpSetCommandValue(handle, nodeName);
    if(DVP_STATUS_OK != nRet){
        printf("%s command %s fail! ErrorCode:[0x%x]\n", mCamConfig.name.toStdString().c_str(), nodeName, nRet);
    }
}

CameraTriggerType CameraDushen::getFrameTriggerMode(const QString& camName)
{
    if(camName != mCamConfig.name)
        return CTT_Off;
    setNodeEnum("TriggerSelector", 6);
    int mode = getNodeEnum("TriggerMode");
    int source = getNodeEnum("TriggerSource");
    return getTriggerMode(mode, source);;
}
FloatValue CameraDushen::getFrameTriggerDelay(const QString& camName)
{
    setNodeEnum("TriggerSelector", 6);
    FloatValue delay = getNodeFloat("TriggerDelay");
    return delay;
}
CameraTriggerType CameraDushen::getLineTriggerMode(const QString& camName)
{
    setNodeEnum("TriggerSelector", 9);
    int mode = getNodeEnum("TriggerMode");
    int source = getNodeEnum("TriggerSource");
    return getTriggerMode(mode, source);
}

CameraTriggerType CameraDushen::getTriggerMode(const int &mode, const int& source)
{
    CameraTriggerType trigMode;
    if(mode == TRIGGER_IN_OFF){
        trigMode = CTT_Off;
    } else {
        switch (source) {
        case TRIGGER_SOURCE_SOFTWARE:
            trigMode = CTT_Software;
            break;
        case TRIGGER_SOURCE_LINE1:
        case TRIGGER_SOURCE_LINE2:
        case TRIGGER_SOURCE_LINE3:
            trigMode = CameraTriggerType(source + CTT_Line0);
            break;
        default:
            trigMode = CTT_Off;
            break;
        }
    }
    return trigMode;
}

IntValue CameraDushen::getWidth(const QString& camName)
{
    return getNodeInt("Width");
}
IntValue CameraDushen::getHeight(const QString& camName)
{
    return getNodeInt("Height");
}
IntValue CameraDushen::getOffsetX(const QString& camName)
{
    return getNodeInt("OffsetX");
}
IntValue CameraDushen::getOffsetY(const QString& camName)
{
    return getNodeInt("OffsetY");
}
bool CameraDushen::getReverseX(const QString& camName)
{
    return getNodeBool("ReverseX");
}
bool CameraDushen::getReverseY(const QString& camName)
{
    return getNodeBool("ReverseY");
}
int CameraDushen::getWhiteBalanceMode(const QString& camName)
{
    return getNodeEnum("BalanceWhiteAuto");
}
IntValue CameraDushen::getWhiteBalanceR(const QString& camName)
{
    setNodeEnum("BalanceRatioSelector", 0);
    return getNodeInt("BalanceRatio");

}
IntValue CameraDushen::getWhiteBalanceG(const QString& camName)
{
    setNodeEnum("BalanceRatioSelector", 1);
    return getNodeInt("BalanceRatio");
}
IntValue CameraDushen::getWhiteBalanceB(const QString& camName)
{
    setNodeEnum("BalanceRatioSelector", 2);
    return getNodeInt("BalanceRatio");
}
int CameraDushen::getExposureAutoMode(const QString& camName)
{
    return  getNodeEnum("ExposureAuto");
    if(!mCamConfig.isLine){
    }
}
FloatValue CameraDushen::getExposureTime(const QString& camName)
{
    return getNodeFloat("ExposureTime");
}
int CameraDushen::getGainMode(const QString& camName)
{
    return getNodeEnum("GainAuto");
}
FloatValue CameraDushen::getGain(const QString& camName)
{
    return getNodeFloat("Gain");
}
bool CameraDushen::getFrameRateControl(const QString& camName)
{
    return getNodeBool("AcquisitionFrameRateEnable");

}
FloatValue CameraDushen::getFrameRate(const QString& camName)
{
    FloatValue rate = getNodeFloat("AcquisitionFrameRate");
    FloatValue rateMax = getNodeFloat("ResultingFrameRate");

    FloatValue value;
    value.val = rate.val;
    value.max = rateMax.val;
    return value;
}

IntValue CameraDushen::getPreDiv(const QString &camName)
{
    return getNodeInt("PreDivider");
}

IntValue CameraDushen::getMul(const QString &camName)
{
    return getNodeInt("Multiplier");
}

IntValue CameraDushen::getPostDiv(const QString &camName)
{
    return getNodeInt("PostDivider");
}

